package combinatorParsing


trait ProperlyTypedParsers extends Tokens {
  /** The parser input is a list of tokens */
  type Input = List[Token]
                      
  def construct[t](components: Parser[Any, Any], fun: PartialFunction[Any, Position => t]) = new SingletonParser[t] {
    def apply(in: Input): ParseResult[t] = components(in) match {
      case s @ Success(out, in1) => Success(fun(out)(in.head.pos), in1) 
      case Failure(msg,in) => Failure(msg, in)
    }
  }
  
  /** A base class for parser results. 
   *  Results are either successes or failures (see below).
   */
  class ParseResult[+t]

  /** The success case of ParseResult.
   *
   *  @param out    The parser's output (which is a list of terminals or non-terminals)
   *  @param in     The parser's remaining input
   */
  case class Success[t](out: t, in: Input) extends ParseResult[t] {
    /** The toString method of a Success yields each result token, separated by newlines */
    override def toString = out.toString //mkString("", "\n", "")
  }

  /** The failure case of ParseResult.
   *
   *  @param msg    An error message string describing the failure
   *  @param in     The parser's unconsumed input at the point where the failure occured
   */
  case class Failure[t](msg: String, in: Input) extends ParseResult[t] {
    /** The toString method of a Failure yields an error message */
    override def toString = "["+in.head.pos+"] error: "+msg+"\n\n"+in.head.pos.longString    
  }
   
  // TODO: is this the right scope?
  val binderStack = new scala.collection.mutable.Stack[Binder[Any]]

  
  /** The root class of parsers. 
   *  Parsers are functions from the Input type to ParseResult 
   */
  abstract class Parser[elementType] { //extends (Input => ParseResult[resultType]) {  
    type resultType
    
    /** An unspecified method the defines the parser function.
     */
    def apply(in: Input): ParseResult[resultType]
  
    /** A parser combinator for sequential composition 
     *
     *  `p && q' succeeds if `p' succeeds and `q' succeeds on the input
     *          left over by `p'.
     */
    def &&[eltUpper >: elementType] (q: => Parser[eltUpper]): Parser[eltUpper]{type resultType=List[eltUpper]}

    // repetition
    def * : ListParser[elementType, List[elementType]] 
  
    // repetition with separator
    def *[s,t](sep: => Parser[s,t]): ListParser[elementType, List[elementType]]
    
    // one ore more occurrences
    def + : ListParser[elementType, List[elementType]]

    // optionality
    def ? : Parser[elementType, Option[elementType]] 
    
    // binders
    def withBinder[res](binder: Binder[Any])(block: => res) :res = {binderStack push binder; val r = block; binderStack.pop; r}  
    def hasBinder[t](x: t): Boolean =  binderStack.elements.exists(.binds(x))
    // TODO: asInstanceOf is okay since binds is only true when binder and bounded element have same type -- make this apparent to the type checker
    def getBinder[t](x: t): Binder[t] = (for(val binder <- binderStack.elements; binder.binds(x)) yield binder).toList.last.asInstanceOf[Binder[t]] 
    
    // declare binder: the binder argument will be initialised so that its scope can be denoted using the ^ combinator
    def !(binder: Binder[elementType]): Parser[elementType, resultType]
    
    // denote scope of a binder
    def ^(binder: Binder[Any]): Parser[elementType, UnderBinder[Any, elementType]] 
    
    // parse something that is susceptible to binding
    def ??[s >: elementType]: Parser[s, Bound[s]] 
    
    type myType 
    /** <p>
     *    A parser combinator for alternative composition 
     *  </p>
     *  <p>
     *    <code>p | q</code> succeeds if <code>p</code> succeeds or
     *    <code>q</code> succeeds.
     *  </p>
     */
    def | (q: => myType): myType     
  }
  
  /** The root class of parsers. 
   *  Parsers are functions from the Input type to ParseResult 
   */
  abstract class SingletonParser[elementType] extends Parser[elementType, elementType] {
    /** A parser combinator for sequential composition 
     *
     *  `p && q' succeeds if `p' succeeds and `q' succeeds on the input
     *          left over by `p'.
     */
    def &&[eltUpper >: elementType] (q: => Parser[eltUpper, eltUpper]) = new ListParser[eltUpper, List[eltUpper]] {
      def apply(in: Input): ParseResult[List[eltUpper]] = SingletonParser.this(in) match {
        case Success(x, in1) => 
          q(in1) match {
            case Success(y, in2) => Success(List(x, y), in2)
            case Failure(msg, in) => Failure(msg, in)
          }
        case Failure(msg, in) => Failure(msg, in)
      }
    }
    // repetition
    def * = new  ListParser[elementType, List[elementType]]  {
      def apply(in1: Input): ParseResult[List[elementType]] = { var result: ParseResult[List[elementType]]=null; 
        var in = in1; var res = new scala.collection.mutable.ListBuffer[elementType];
        while(result==null) {
          SingletonParser.this(in) match {
            case Success(x, in1) => in = in1; res += x
            case Failure(msg, in1) => result=Success(res.toList, in1)
          }
        }
        result
      }
    }
  
    // repetition with separator
    def *[s,t](sep: => Parser[s,t]) = new ListParser[elementType, List[elementType]]  {
      def apply(in1: Input): ParseResult[List[elementType]] = { var result: ParseResult[List[elementType]]=null; 
        var in = in1; var res = new scala.collection.mutable.ListBuffer[elementType]; var ok=true
        while(result==null) {
          SingletonParser.this(in) match {
            case Success(x, in1) => res += x; ok=true
                                    sep(in1) match {
                                      case Success(_, in1) => in=in1; ok=false
                                      case Failure(msg, in1) => result=Success(res.toList, in1)
                                    }
            case Failure(msg, in1) => result= if(ok) Success(res.toList, in1) else Failure(msg+" (malformed separated repetition: no element after separator)", in1)
          }
        }
        result
      }      
    }
    
    // one ore more occurrences
    def + = new ListParser[elementType, List[elementType]]   {
      def apply(in1: Input): ParseResult[List[elementType]] = { var result: ParseResult[List[elementType]]=null; 
        var in = in1; var res = new scala.collection.mutable.ListBuffer[elementType]; var ok = false
        while(result==null) {
          SingletonParser.this(in) match {
            case Success(x, in1) => in = in1; res += x; ok=true
            case Failure(msg, in1) => result= if(ok) Success(res.toList, in1) else Failure(msg, in1)
          }
        }
        result
      }      
    }

    // optionality
    def ? = new  Parser[elementType, Option[elementType]]   {
      def apply(in: Input): ParseResult[Option[elementType]] = { 
        SingletonParser.this(in) match {
          case Success(x, in1) => Success(Some(x), in1)
          case Failure(msg, in1) => Success(None, in1)
        }
      }
    }
        
    // declare binder: the binder argument will be initialised so that its scope can be denoted using the ^ combinator
    def !(binder: Binder[elementType]) = new  Parser[elementType, elementType]  {
      def apply(in: Input): ParseResult[elementType] = { 
        SingletonParser.this(in) match {
          case s @ Success(x, in1) => binder.binderElement=Some(x); s
          case f => f
        }
      }
    }
    
    // denote scope of a binder
    def ^(binder: Binder[Any])  = new Parser[elementType, UnderBinder[Any, elementType]] {
      def apply(in: Input): ParseResult[UnderBinder[Any, elementType]] =  withBinder(binder){
        SingletonParser.this(in) match {
            case Success(x, in1) => Success(new UnderBinder(binder, x)(in.head.pos), in1)
            case Failure(msg, in) => Failure(msg, in)
          }
        }
    }
    
    // parse something that is susceptible to binding
    def ??[s >: elementType] = new Parser[s, Bound[s]] {
      def apply(in: Input): ParseResult[Bound[s]] = { 
        SingletonParser.this(in) match {
          case Success(x, in1) if hasBinder(x) => Success(Bound[s](x, getBinder(x))(in.head.pos), in1) 
          case Success(x, in1) => Failure("unbound variable:  "+x, in1)
          case Failure(msg, in) =>  Failure(msg, in)
        }
      }
    }
    
    type myType = SingletonParser[elementType]
    /** <p>
     *    A parser combinator for alternative composition 
     *  </p>
     *  <p>
     *    <code>p | q</code> succeeds if <code>p</code> succeeds or
     *    <code>q</code> succeeds.
     *  </p>
     */
    def | (q: => myType): myType     
      = new SingletonParser[elementType] {
          def apply(in: Input): ParseResult[elementType] = SingletonParser.this(in) match {
            case s @ Success(out, rest) => s
            case Failure(msg, in1) => q(in) match {
              case s2 @ Success(_, _) => s2
              case f2 @ Failure(_, in2) => if (in2.head.pos < in1.head.pos) Failure(msg, in1) else f2
            }
          }
        }
  }  
  
  abstract class ListParser[elementType, +resultType <: List[elementType]] extends Parser[elementType, resultType] {
    override def &&[eltUpper >: elementType] (q: => Parser[eltUpper, eltUpper]) = new ListParser[eltUpper, List[eltUpper]] {
      def apply(in: Input): ParseResult[List[eltUpper]] = ListParser.this(in) match {
        case Success(x, in1) => 
          q(in1) match {
            case Success(y, in2) => Success(List.concat(x, List(y)), in2) 
            case Failure(msg, in) => Failure(msg, in)
          }
        case Failure(msg, in) => Failure(msg, in)
      }
    }
  }
 
}